/*
 *  * Copyright 2000-2009
 *   * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *    *
 *     * SPDX-License-Identifier:	GPL-2.0+
 *     */
#include <config.h>
#include <common.h>
#include <sparse_format.h>
#include "sparse.h"
#include "../sprite_verify.h"
#include "sunxi_flash.h"
#include "../cartoon/sprite_cartoon.h"

#define SPARSE_FORMAT_TYPE_TOTAL_HEAD 0xff00
#define SPARSE_FORMAT_TYPE_CHUNK_HEAD 0xff01
#define SPARSE_FORMAT_TYPE_CHUNK_DATA 0xff02
#define SPARSE_FORMAT_TYPE_CHUNK_FILL_DATA 0xff03

static uint android_format_checksum;
static uint sparse_format_type;
static uint chunk_count;
static int last_rest_size;
static int chunk_length;
static uint flash_start;
static sparse_header_t globl_header;
static uint total_chunks;
/*
************************************************************************************************************
*
*                                             unsparse_probe
*
*    函数名称：
*
*    参数列表：
*
*    返回值  ：
*
*    说明    ：
*
*
************************************************************************************************************
*/
int unsparse_probe(char *source, uint length, uint android_format_flash_start)
{
	sparse_header_t *header = (sparse_header_t *)source;

	if (header->magic != SPARSE_HEADER_MAGIC) {
		printf("sparse: bad magic\n");

		return ANDROID_FORMAT_BAD;
	}

	if ((header->major_version != SPARSE_HEADER_MAJOR_VER) ||
	    (header->file_hdr_sz != sizeof(sparse_header_t)) ||
	    (header->chunk_hdr_sz != sizeof(chunk_header_t))) {
		printf("sparse: incompatible format\n");

		return ANDROID_FORMAT_BAD;
	}
	android_format_checksum = 0;
	last_rest_size		= 0;
	chunk_count		= 0;
	chunk_length		= 0;
	sparse_format_type      = SPARSE_FORMAT_TYPE_TOTAL_HEAD;
	flash_start		= android_format_flash_start;
	total_chunks		= header->total_chunks;

	return ANDROID_FORMAT_DETECT;
}
/*
************************************************************************************************************
*
*                                             DRAM_Write
*
*    函数名称：
*
*    参数列表：
*
*    返回值  ：
*
*    说明    ：
*
*
************************************************************************************************************
*/
int unsparse_direct_write(void *pbuf, uint length)
{
	int unenough_length;
	int this_rest_size;
	int tmp_down_size;
	char *tmp_buf, *tmp_dest_buf;
	chunk_header_t *chunk;
	//首先计算传进的数据的校验和
	android_format_checksum += add_sum(pbuf, length);

	this_rest_size = last_rest_size + length;
	tmp_buf	= (char *)pbuf - last_rest_size;
	last_rest_size = 0;
	while (this_rest_size > 0) {
		switch (sparse_format_type) {
		case SPARSE_FORMAT_TYPE_TOTAL_HEAD: {
			memcpy(&globl_header, tmp_buf, sizeof(sparse_header_t));
			this_rest_size -= sizeof(sparse_header_t);
			tmp_buf += sizeof(sparse_header_t);

			sparse_format_type = SPARSE_FORMAT_TYPE_CHUNK_HEAD;

			break;
		}
		case SPARSE_FORMAT_TYPE_CHUNK_HEAD: {
			if (this_rest_size < sizeof(chunk_header_t)) {
				printf("sparse: chunk head data is not enough\n");
				last_rest_size = this_rest_size;
				tmp_dest_buf   = (char *)pbuf - this_rest_size;
				memcpy(tmp_dest_buf, tmp_buf, this_rest_size);
				this_rest_size = 0;

				break;
			}
			chunk = (chunk_header_t *)tmp_buf;
			/* move to next chunk */
			tmp_buf += sizeof(
				chunk_header_t); //此时tmp_buf已经指向下一个chunk或者data起始地址
			this_rest_size -=
				sizeof(chunk_header_t); //剩余的数据长度
			chunk_length =
				chunk->chunk_sz *
				globl_header
					.blk_sz; //当前数据块需要写入的数据长度
			printf("chunk %d(%d)\n", chunk_count++, total_chunks);
#ifdef CONFIG_SUNXI_SPRITE_CARTOON
			sprite_cartoon_upgrade(10 + (70 * chunk_count)/total_chunks);
#endif
			switch (chunk->chunk_type) {
			case CHUNK_TYPE_RAW:

				if (chunk->total_sz !=
				    (chunk_length + sizeof(chunk_header_t))) {
					printf("sparse: bad chunk size for chunk %d, type Raw\n",
					       chunk_count);

					return -1;
				}
				//这里不处理数据部分，转到下一个状态
				sparse_format_type =
					SPARSE_FORMAT_TYPE_CHUNK_DATA;

				break;

			case CHUNK_TYPE_FILL:
				if (chunk->total_sz !=
				    sizeof(chunk_header_t) + sizeof(u32)) {
					printf("spase : bad chunk size for chunk ,type FILL \n");
					return -1;
				}
				sparse_format_type =
					SPARSE_FORMAT_TYPE_CHUNK_FILL_DATA;

				break;
			case CHUNK_TYPE_DONT_CARE:
				if (chunk->total_sz != sizeof(chunk_header_t)) {
					printf("sparse: bogus DONT CARE chunk\n");

					return -1;
				}
				flash_start += (chunk_length >> 9);
				sparse_format_type =
					SPARSE_FORMAT_TYPE_CHUNK_HEAD;

				break;

			default:
				printf("sparse: unknown chunk ID %x\n",
				       chunk->chunk_type);

				return -1;
			}
			break;
		}
		case SPARSE_FORMAT_TYPE_CHUNK_DATA: {
			//首先判断数据是否足够当前chunk所需,如果不足，则计算出还需要的数据长度
			unenough_length =
				(chunk_length >= this_rest_size) ?
					(chunk_length - this_rest_size) :
					0;
			if (!unenough_length) {
				//数据足够，直接写入
				if (!sunxi_sprite_write(flash_start,
							chunk_length >> 9,
							tmp_buf)) {
					printf("sparse: flash write failed\n");
					return -1;
				}
				if (chunk_length & 511) {
					printf("data is not sector align 0\n");

					return -1;
				}
				flash_start += (chunk_length >> 9);
				tmp_buf += chunk_length;
				this_rest_size -= chunk_length;
				chunk_length = 0;

				sparse_format_type =
					SPARSE_FORMAT_TYPE_CHUNK_HEAD;
			} else { //存在缺失数据的情况
				if (this_rest_size < 8 * 1024) {
					//先看已有数据是否不足8k
					//当不足时，把这笔数据放到下一笔数据的前部，等待下一次处理
					tmp_dest_buf =
						(char *)pbuf - this_rest_size;
					memcpy(tmp_dest_buf, tmp_buf,
					       this_rest_size);
					last_rest_size = this_rest_size;
					this_rest_size = 0;

					break;
				}
				//当已有数据超过16k时
				//当缺失数据长度不足4k时,可能只缺几十个字节
				if (unenough_length < 4 * 1024) {
					//采用拼接方法，先烧写部分已有数据，然后在下一次把未烧写的已有数据和缺失数据一起烧录
					tmp_down_size = this_rest_size +
							unenough_length -
							4 * 1024;
				} else {
					//这里处理缺失数据超过8k(包含)的情况,同时已有数据也超过16k

					//直接烧录当前全部数据;
					tmp_down_size = this_rest_size &
							(~(512 - 1)); //扇区对齐
				}
				if (!sunxi_sprite_write(flash_start,
							tmp_down_size >> 9,
							tmp_buf)) {
					printf("sparse: flash write failed\n");
					return -1;
				}
				if (tmp_down_size & 511) {
					printf("data is not sector align 1\n");

					return -1;
				}
				tmp_buf += tmp_down_size;
				flash_start += (tmp_down_size >> 9);
				chunk_length -= tmp_down_size;
				this_rest_size -= tmp_down_size;
				tmp_dest_buf = (char *)pbuf - this_rest_size;
				memcpy(tmp_dest_buf, tmp_buf, this_rest_size);
				last_rest_size = this_rest_size;
				this_rest_size = 0;

				sparse_format_type =
					SPARSE_FORMAT_TYPE_CHUNK_DATA;
			}

			break;
		}
		case SPARSE_FORMAT_TYPE_CHUNK_FILL_DATA: {
			u32 fillbuf[1024];
			u32 file_val = 0;
			u32 ii       = 0;

			if (this_rest_size >= 4) {
				this_rest_size -= sizeof(u32);
				file_val = *(int *)tmp_buf;
				if (chunk_length & 511) {
					printf("fill data is not sector align 0\n");
					return -1;
				}
				for (ii = 0; ii < sizeof(fillbuf)/sizeof(fillbuf[0]); ii++)
					fillbuf[ii] = file_val;
				for (ii = 0; ii < (chunk_length >> 12); ii++) {
					if (!sunxi_sprite_write(flash_start, 8,
								fillbuf)) {
						printf("sparse: fill data write failed\n");

						return -1;
					}
					flash_start += 8;
				}
				tmp_buf += sizeof(u32);
				sparse_format_type =
					SPARSE_FORMAT_TYPE_CHUNK_HEAD;
			} else {
				tmp_dest_buf = (char *)pbuf - this_rest_size;
				memcpy(tmp_dest_buf, tmp_buf, this_rest_size);
				last_rest_size = this_rest_size;
				this_rest_size = 0;
				sparse_format_type =
					SPARSE_FORMAT_TYPE_CHUNK_FILL_DATA;
			}

			break;
		}
		default: {
			printf("sparse: unknown status\n");

			return -1;
		}
		}
	}

	return 0;
}
/*
************************************************************************************************************
*
*                                             unsparse_checksum
*
*    函数名称：
*
*    参数列表：
*
*    返回值  ：
*
*    说明    ：
*
*
************************************************************************************************************
*/
uint unsparse_checksum(void)
{
	return android_format_checksum;
}
